# -*- coding: utf-8 -*-
import base64
import hashlib
import json
import random
import string
import time

import requests
from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5
from Crypto.Hash import MD5
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5 as Signature_pkcs1_v1_5
from django.conf import settings

from async import async_job
from common.order import db as order_db
from common.order.model import PAY_STATUS
from common.utils import track_logging
from common.utils.exceptions import ParamError

_LOGGER = track_logging.getLogger(__name__)

_GATEWAY = 'http://api.pocopayment.com/v1?method={}&pid={}&randstr={}&sign={}&timestamp={}'

APP_CONF = {
    '1180119181313637': {
        'API_KEY': 'f48d37f2e6d28d2fd937634bfc4615e8',
        'pid': '10803255000000754615',
        'pub_key': """-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDjWPpLxJMgAblIm7BUseBOBYgZ
v/P40vLmr1sGDk4LaACcQbjg8bE8JNBy79iXjrYeHmGrBVfdx8juwTXI+hNYOxmt
YzSTu3LMnZ4qqqFAs4apHyv0O7/YrQdKKkMIX0NTr3rlnCCds/+i2FeTb+n7kYJq
WIe54NmLs+VYosUQyQIDAQAB
-----END PUBLIC KEY-----""",
        'pri_key': """-----BEGIN ENCRYPTED PRIVATE KEY-----
MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI9nJEYAqr6OoCAggA
MBQGCCqGSIb3DQMHBAjCOOumC9RhAwSCAoBw/hrtfPaZQDiVMc947rVvlrnV5Nyg
n/5CEDOVTo4VEvDaOin2kR/+4xkbKT0M42y0wO1mvM9xiTKCE4MBloaNpJFQhPdi
7HIQfodRPTtvixxdBr1rkaWhgCcUiljJGerrXOaHSAS6Wd2i1OnyobWh+iibZYqp
gm7YU+q4kjM1kOMEBnKurHvohU5Bqh20WWlFKRhRkCbaRQYP6iyUHjvtJF0+8zuf
UsGjIB0I1lq0DHiaILp4xW//fUws/CYVEKj6CQ1NFNvpJbxB13XT72qIfGIzJzon
9a1wOvTlLg8MXKKApKRcAUo8h+wZokA/LwxGBxDPT+TdF9J/JEhKW9tt/8YZZyUd
yU6rMO5Xxylx7hKlLtfAg5WVqK3jEUHcIy8TkHz0aQjLeSwJYYDEOWUVKwtzx8jC
rMedJIn1kXJ/jnNQqP0tahaFaRUsBvATQPi3hxRFhuc+zhyrHa6TgFg8b385Sx59
HG0IdT5O2qOgrg/0ua30WpJdoEtHvZtAAnAONoOnF1i65ZCf/mEJdVvD3QAXiCt0
3zQxRSbhEbZSzp/5fZWyVgMHo1wDw1rl5SFq4cE2mefj/Iany0CmKcUfQEiV9kA6
QTihhlmUOXUEgIr1HWJNpAyrgwkodE08qtPaqJyl0Ob+voagLxS94Vp1TJYakCxC
doFJcQjKinJR+kJM2nnisH3PUbM6bGHDFMnp1xkT0spkW01O//nLMkdP4j0BJT0U
01LH8x0l+wFLrbiGSVcKQPhPTKWWXMGX55Yd065T089V66dz4lnHJHetqXpGNvx8
vXlHhaWTwOXSAQHJCXY5D4a6+S90XPhMMejOgtBUIq8JmjBhw8/bQ+R2
-----END ENCRYPTED PRIVATE KEY-----""",
    },
    '1180505191735716': {
        'API_KEY': 'd098068949a0d4298cdb9b6ed82c61ab',
        'pid': '10803833000000380629',
        'pub_key': """-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDrnaKlmHg6vNpgVttlvrWig3MA
/4QfjKUsI6Sc7pFtpMlER+v5zbwzfPWHxKN+pte9OR261lbExw900QcfE24Y4Wbv
lw/0WDsw+AYdx4tYsFiXCpZ9JXN0PBH0jjWT1XLQQQf6oiVzsmURKhipmtqDhcWs
sfCsE11IycT0qiWnxwIDAQAB
-----END PUBLIC KEY-----""",
        'pri_key': """-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOudoqWYeDq82mBW
22W+taKDcwD/hB+MpSwjpJzukW2kyURH6/nNvDN89YfEo36m1705HbrWVsTHD3TR
Bx8TbhjhZu+XD/RYOzD4Bh3Hi1iwWJcKln0lc3Q8EfSONZPVctBBB/qiJXOyZREq
GKma2oOFxayx8KwTXUjJxPSqJafHAgMBAAECgYA90oLWyIbjgqIaHTA8Jioqlisa
2/CjBphgWInc04w9JLvaurRkO2NibytNkH8FwGljMRVn7M2fnEul7yLbOJHuXF5V
qjbx4ZZOqCN/cgWo3zi5/aTrsSCXF30QUTMXeWh0OFLresQQ/7JIfT6IDLfccRQ0
Lw5YlmkTPubRKslOYQJBAP6nBYYuE6aXl5c2Ti1G4ULmOsHMIhyuLvdEqf8+QWB3
qcGkUFkLletl/08pOI3sfjX28awGjg/quJhYLgleGlUCQQDs3NMhqn73bWf50ea+
k2KShZOhHg+stDyDkGAzVFPCdHyIsRTaSaC+UBzg9t2ttnHLqMd61KAdvkjaoba0
ZM2rAkEAgy3Gyrgxpz+v91UiMHCy2aN/4RUm/V89EmNsru+C/g6mg/oA6d3t/LUu
L7ksSO/qlCIBnHxD8i8XqGLEVE8FgQJAIDAaGPccbe9WM+LMYpWQzayNVVXKd4O+
vXo/OHp7L2+5BLJbzRY4W+RzKDckBNlIOY7kHNMMNz6Gk/9p3rXcJQJAaGocYgDE
qocCWAXoL1UoWuue7lnu8Cd8hh9VHEzcWScfukIOM19oF90qdg5jFXW5n8LIAK09
ITNDI0lQGaOdMg==
-----END PRIVATE KEY-----""",
    },
}


def _get_api_key(mch_id):
    return APP_CONF[mch_id]['API_KEY']


def _get_pid(mch_id):
    return APP_CONF[mch_id]['pid']


def _get_pub_key(mch_id):
    return APP_CONF[mch_id]['pub_key']


def _get_pri_key(mch_id):
    return APP_CONF[mch_id]['pri_key']


def rsa_long_encrypt(pub_key_str, msg, length=100):
    """
    单次加密串的长度最大为 (key_size/8)-11
    1024bit的证书用100， 2048bit的证书用 200
    """
    pubobj = RSA.importKey(pub_key_str)
    pubobj = Cipher_pkcs1_v1_5.new(pubobj)
    res = []
    for i in range(0, len(msg), length):
        res.append(pubobj.encrypt(msg[i:i + length]))
    return "".join(res)


def rsa_long_decrypt(priv_key_str, msg, length=128):
    """
    1024bit的证书用128，2048bit证书用256位
    """
    privobj = RSA.importKey(priv_key_str, passphrase='614360')
    privobj = Cipher_pkcs1_v1_5.new(privobj)
    res = []
    for i in range(0, len(msg), length):
        res.append(privobj.decrypt(msg[i:i + length], 'xyz'))
    return "".join(res)


# def _gen_sign(message):
#     key = RSA.importKey(_my_private_key)
#     h = MD5.new(message)
#     signer = Signature_pkcs1_v1_5.new(key)
#     signature = signer.sign(h)
#     return base64.b64encode(signature)


def _verify_sign(data, signature, key):
    key = RSA.importKey(key)
    h = MD5.new(data)
    verifier = Signature_pkcs1_v1_5.new(key)
    if verifier.verify(h, base64.b64decode(signature)):
        return True
    return False


def _verify_sign1(data, signature, key):
    key = RSA.importKey(key)
    digetst = MD5.new()
    digetst.update(data.encode('utf8'))
    verifier = Signature_pkcs1_v1_5.new(key)
    if verifier.verify(digetst, bytes(signature, 'utf-8')):
        return True
    return False


def generate_rsa_sign_str(parameter):
    s = ''
    for k in sorted(parameter.keys()):
        s += '%s=%s&' % (k.encode('utf8'), parameter[k].encode('utf8'))
    return s[0:len(s) - 1]


def _gen_sign(s):
    m = hashlib.md5()
    m.update(s.encode('utf8'))
    sign = m.hexdigest().lower()
    return sign


def _get_device_ip(info):
    try:
        extra = json.loads(info['extra'])
    except:
        extra = {}
    user_info = extra.get('user_info', {})
    return user_info.get('device_ip') or '127.0.0.1'


# 支付方式 微信:weixin QQ:qq 支付宝:alipay
def _get_pay_type(service):
    if service == 'wxpay':
        return 'weixin'
    elif service == 'qq':
        return 'qq'
    elif service == 'alipay':
        return 'alipay'
    else:
        return 'jd'  # jd


def _fix_pay_amount(pay, pay_amount):
    extend = json.loads(pay.extend or '{}')
    if pay_amount >= 10 and int(pay_amount) == pay_amount:
        discount = random.randint(1, 10)
        pay_amount = pay_amount - float(discount) / 100
        extend.update({'discount': str(float(discount) / 100)})
        order_db.fill_extend(pay.id, extend)
    return pay_amount


def create_charge(pay, pay_amount, info):
    ''' 创建订单 '''
    # pay_amount = _fix_pay_amount(pay, pay_amount)
    app_id = info['app_id']
    api_key = _get_api_key(app_id)
    service = info.get('service')
    get_data = {
        'pid': _get_pid(app_id),
        'method': 'com.post.merchant.wap.wapPay.create',
        'timestamp': str(int(time.time())),
        'randstr': ''.join(random.sample(string.ascii_letters + string.digits, 32)),
    }
    post_data = {
        'out_trade_no': str(pay.id),
        'body': 'qwe',
        'pay_type': _get_pay_type(service),
        'trade_amount': str(int(pay_amount * 100)),
        'notify_url': '{}/pay/api/{}/doudoupay/{}'.format(settings.NOTIFY_PREFIX, settings.NOTIFY_PATH, app_id),
        'sync_url': '{}/pay/api/{}/doudoupay/{}/'.format(settings.NOTIFY_PREFIX, settings.RETURN_PATH, pay.id),
        'client_ip': _get_device_ip(info),
        'merchant_no': app_id,
    }
    get_data['data'] = post_data
    j = json.dumps(get_data, sort_keys=True, separators=(',', ':'))
    sign = _gen_sign(j + api_key)
    url = _GATEWAY.format(get_data['method'], get_data['pid'], get_data['randstr'], sign, get_data['timestamp'])
    j_p = json.dumps(post_data, sort_keys=True, separators=(',', ':'))
    d = rsa_long_encrypt(_get_pub_key(app_id), j_p, 117)
    b = base64.b64encode(d)
    _LOGGER.info("doudoupay create  url: %s, data: %s", url, j_p)
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    response = requests.post(url, data='data=' + b, headers=headers, timeout=3)
    _LOGGER.info("doudoupay create rdp data: %s", response.text)
    rsp_obj = json.loads(response.text)
    return {'charge_info': rsp_obj['data']['out_pay_url']}


def generate_sign(parameter, key):
    s = ''
    for k in sorted(parameter.keys()):
        if parameter[k] != '' and parameter[k] != None:
            s += '%s=%s&' % (k, parameter[k])
    s += 'key=%s' % key
    m = hashlib.md5()
    m.update(s.encode('utf8'))
    sign = m.hexdigest().lower()
    return sign


def verify_notify_sign(params, key):
    sign = params['sign']
    params.pop('sign')
    calculated_sign = generate_sign(params, key)
    if sign != calculated_sign:
        _LOGGER.info("doudoupay sign: %s, calculated sign: %s", sign, calculated_sign)
        raise ParamError('sign not pass, data: %s' % params)


# SUCCEED
def check_notify_sign(request, app_id):
    api_key = _get_api_key(app_id)
    data = dict(request.POST.iteritems())
    _LOGGER.info("doudoupay notify data: %s", data)
    verify_notify_sign(data, api_key)
    pay_id = data['out_trade_no']
    if not pay_id:
        _LOGGER.error("fatal error, out_trade_no not exists, data: %s" % data)
        raise ParamError('doudoupay event does not contain pay ID')

    pay = order_db.get_pay(int(pay_id))
    if not pay:
        raise ParamError('pay_id: %s invalid' % pay_id)
    if pay.status != PAY_STATUS.READY:
        raise ParamError('pay %s has been processed' % pay_id)

    mch_id = pay.mch_id
    trade_status = data[u'trade_result']
    trade_no = data[u'trade_no']
    total_fee = float(data[u'trade_amount']) / 100.0
    discount = float(json.loads(pay.extend or '{}').get('discount', 0))
    extend = {
        'discount': discount,
        'trade_status': trade_status,
        'trade_no': trade_no,
        'total_fee': total_fee
    }

    from common.channel.pay import check_channel_order
    check_channel_order(pay_id, total_fee, app_id)

    if trade_status == 'SUCCESS':
        _LOGGER.info('doudoupay check order success, mch_id:%s pay_id:%s' % (mch_id, pay_id))
        order_db.add_pay_success(mch_id, pay_id, total_fee, trade_no, extend)
        # async notify
        async_job.notify_mch(pay_id)


def query_charge(pay_order, app_id):
    ''' 查询订单 '''
    api_key = _get_api_key(app_id)
    get_data = {
        'pid': _get_pid(app_id),
        'method': 'com.post.merchant.wap.wapPay.query',
        'timestamp': str(int(time.time())),
        'randstr': ''.join(random.sample(string.ascii_letters + string.digits, 32)),
    }
    post_data = {
        'merchant_no': app_id,
        'out_trade_no': str(pay_order.id),
    }
    get_data['data'] = post_data
    j = json.dumps(get_data, sort_keys=True, separators=(',', ':'))
    sign = _gen_sign(j + api_key)
    url = _GATEWAY.format(get_data['method'], get_data['pid'], get_data['randstr'], sign, get_data['timestamp'])
    j_p = json.dumps(post_data, sort_keys=True, separators=(',', ':'))
    d = rsa_long_encrypt(_get_pub_key(app_id), j_p, 117)
    b = base64.b64encode(d)
    _LOGGER.info("doudoupay query  url: %s, data: %s", url, j_p)
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    response = requests.post(url, data='data=' + b, headers=headers, timeout=3)
    _LOGGER.info("doudoupay query rsp: %s", response.text)
    if response.status_code == 200:
        data = json.loads(response.text)['data']
        mch_id = pay_order.mch_id
        trade_status = data['trade_status']
        total_fee = float(data['trade_amount']) / 100.0
        trade_no = ''
        discount = float(json.loads(pay_order.extend or '{}').get('discount', 0))
        extend = {
            'discount': discount,
            'trade_status': trade_status,
            'total_fee': total_fee,
            'trade_no': trade_no,
        }

        if trade_status == 'ORDER_SUCCESS' and total_fee > 0:
            _LOGGER.info('doudoupay query order success, mch_id:%s pay_id:%s' % (mch_id, pay_order.id))
            order_db.add_pay_success(pay_order.mch_id, pay_order.id,
                                     total_fee, trade_no, extend)

            async_job.notify_mch(pay_order.id)
